package thread

import (
	"fmt"
	"strings"
	"time"

	"gitee.com/sansaniot/ssiot-core/facade/env"
	"gitee.com/sansaniot/ssiot-core/logger"

	"ssdevice/common/constant"
	"ssdevice/common/utils"
)

type DevSensorUpdateThread struct {
}

type senUpdate struct {
	Sn      string
	Key     string
	DataStr string
	TimeStr string
}

func (DevSensorUpdateThread) Run(threadName string) {
	logger.Infof("启动线程 - %s.", threadName)

	go func() {
		dataList := make([]senUpdate, 0)
		for {
			// 缓存慢更新, 查询查的缓存
			time.Sleep(time.Duration(constant.CacheFlushSecInterval) * time.Second)
			// 遍历
			constant.SensorCache.IterCb(func(sn string, value map[string]interface{}) {
				for k, v := range value {
					vitalK := sn + "#" + k
					// 仅更新需要更新的sn、key、data ,  pop 下次不用更新了
					uptime, ok := constant.SensorSnKey.Pop(vitalK)
					if !ok {
						continue
					}
					su := senUpdate{
						Sn:      sn,
						Key:     k,
						DataStr: string(utils.MapToBytes(map[string]interface{}{k: v})),
						TimeStr: uptime,
					}
					dataList = append(dataList, su)

					// 批量更新
					if len(dataList) > constant.ThreadBatchSize {
						// 批量处理数据
						dealDevSensorData(dataList)
						// 清空(减少扩容)
						dataList = dataList[0:0]
					}
				}
			})

			if len(dataList) > 0 {
				// 批量处理数据
				dealDevSensorData(dataList)
				// 清空(减少扩容)
				dataList = dataList[0:0]
			}
		}
	}()
}

var scan = map[string]interface{}{}

func dealDevSensorData(dataList []senUpdate) {
	t1 := time.Now()
	sql := "UPDATE sys_dev_sensor SET data = tmp.data, update_time = to_timestamp(tmp.uptime,'yyyy-MM-DD hh24:mi:ss') FROM (VALUES %s) as tmp (sn, key, data, uptime) where sys_dev_sensor.sn = tmp.sn and sys_dev_sensor.key = tmp.key"
	valueSql := make([]string, 0)
	for _, s := range dataList {
		valueSql = append(valueSql, "('"+s.Sn+"','"+s.Key+"', '"+s.DataStr+"'::jsonb,'"+s.TimeStr+"')")
	}
	sql = fmt.Sprintf(sql, strings.Join(valueSql, ","))
	if err := env.Db.Raw(sql).Scan(&scan).Error; err != nil {
		logger.Error(err)
	}
	logger.Infof("实时sensor批量更新%d. 耗时%v", len(dataList), time.Since(t1).Round(time.Millisecond))
}
